/* Author: Anirudh Sasikumar */
/* URL: http://anirudhs.chaosnet.org/ */
/* Project URL: http://code.google.com/p/fxstruts/ */

/* FxStruts Taglib is licensed under the Apache License, Version
 * 2.0. It is based on code in MessageTag.java part of struts-taglib
 * licensed under the Apache Software License 2.0.  */

/* Copyright 2008 Anirudh Sasikumar */

/* Licensed under the Apache License, Version 2.0 (the "License"); you
 * may not use this file except in compliance with the License. You
 * may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0  */

/* Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied. See the License for the specific language governing
 * permissions and limitations under the License.  */

/*  ____    ___                     */
/* /\  _`\ /\_ \                    */
/* \ \ \L\_\//\ \      __   __  _   */
/*  \ \  _\/ \ \ \   /'__`\/\ \/'\  */
/*   \ \ \/   \_\ \_/\  __/\/>  </  */
/*    \ \_\   /\____\ \____\/\_/\_\ */
/*     \/_/   \/____/\/____/\//\/_/ */

package org.fxstruts.taglib;

import org.apache.struts.Globals;
import org.apache.struts.action.ActionMessage;
import org.apache.struts.action.ActionMessages;
import org.apache.struts.taglib.TagUtils;
import org.apache.struts.util.MessageResources;
import org.fxstruts.xml.FxXMLOutput;


import flex.messaging.io.SerializationContext;
import flex.messaging.io.amf.Amf3Output;

import javax.servlet.http.HttpSession;
import javax.servlet.jsp.JspException;
import javax.servlet.jsp.tagext.TagSupport;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Locale;

/**
 * Custom tag that retrieves an internationalized messages string (with
 * optional parametric replacement) from the <code>ActionResources</code>
 * object stored as a context attribute by our associated
 * <code>ActionServlet</code> implementation and outputs it as AMF or XML.
 *
 * @version $Rev: 471754 $ $Date: 2005-09-16 09:38:33 -0400 (Fri, 16 Sep 2005)
 *          $
 */
public class BeanMessageTag extends TagSupport {
	
    /**
     * The message resources for this package.
     */
    protected static MessageResources messages =
        MessageResources.getMessageResources(
            "org.apache.struts.taglib.bean.LocalStrings");

    // ------------------------------------------------------------- Properties
    
    protected String type = "amf";
    
    /**
     * The first optional argument.
     */
    protected String arg0 = null;

    /**
     * The second optional argument.
     */
    protected String arg1 = null;

    /**
     * The third optional argument.
     */
    protected String arg2 = null;

    /**
     * The fourth optional argument.
     */
    protected String arg3 = null;

    /**
     * The fifth optional argument.
     */
    protected String arg4 = null;

    /**
     * The servlet context attribute key for our resources.
     */
    protected String bundle = null;

    /**
     * The message key of the message to be retrieved.
     */
    protected String key = null;

    /**
     * Name of the bean that contains the message key.
     */
    protected String name = null;

    /**
     * Name of the property to be accessed on the specified bean.
     */
    protected String property = null;

    /**
     * The scope to be searched to retrieve the specified bean.
     */
    protected String scope = null;

    /**
     * The session scope key under which our Locale is stored.
     */
    protected String localeKey = Globals.LOCALE_KEY;
    
    protected boolean errors = false;

    protected boolean token = false;

    /**
     * The session attribute key for our locale.
     */
    protected String locale = Globals.LOCALE_KEY;

    /**
     * The request attribute key for our error messages (if any).
     */
    protected String errorname = Globals.ERROR_KEY;

    /**
     * The name of the property for which error messages should be returned,
     * or <code>null</code> to return all errors.
     */
    protected String errorproperty = null;

    /**
     * The message resource key for errors header.
     */
    protected String header = null;

    /**
     * The message resource key for errors footer.
     */
    protected String footer = null;

    /**
     * The message resource key for errors prefix.
     */
    protected String prefix = null;

    /**
     * The message resource key for errors suffix.
     */
    protected String suffix = null;

    public String getArg0() {
        return (this.arg0);
    }

    public void setArg0(String arg0) {
        this.arg0 = arg0;
    }

    public String getArg1() {
        return (this.arg1);
    }

    public void setArg1(String arg1) {
        this.arg1 = arg1;
    }

    public String getArg2() {
        return (this.arg2);
    }

    public void setArg2(String arg2) {
        this.arg2 = arg2;
    }

    public String getArg3() {
        return (this.arg3);
    }

    public void setArg3(String arg3) {
        this.arg3 = arg3;
    }

    public String getArg4() {
        return (this.arg4);
    }

    public void setArg4(String arg4) {
        this.arg4 = arg4;
    }

    public String getBundle() {
        return (this.bundle);
    }

    public void setBundle(String bundle) {
        this.bundle = bundle;
    }

    public String getKey() {
        return (this.key);
    }

    public void setKey(String key) {
        this.key = key;
    }

    public String getName() {
        return (this.name);
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getProperty() {
        return (this.property);
    }

    public void setProperty(String property) {
        this.property = property;
    }

    public String getScope() {
        return (this.scope);
    }

    public void setScope(String scope) {
        this.scope = scope;
    }

    public String getLocale() {
        return (this.localeKey);
    }

    public void setLocale(String localeKey) {
        this.localeKey = localeKey;
    }

    public String getType() {
        return (this.type);
    }

    public void setType(String type) {
        this.type = type;
    }
    
    public String getErrorname() {
        return (this.name);
    }

    public void setErrorname(String name) {
        this.name = name;
    }

    public String getErrorproperty() {
        return (this.property);
    }

    public void setErrorproperty(String property) {
        this.property = property;
    }

    public String getHeader() {
        return (header == null) ? "errors.header" : header;
    }

    public void setHeader(String header) {
        this.header = header;
    }

    public String getFooter() {
        return (footer == null) ? "errors.footer" : footer;
    }

    public void setFooter(String footer) {
        this.footer = footer;
    }

    public String getPrefix() {
        return (prefix == null) ? "errors.prefix" : prefix;
    }

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public String getSuffix() {
        return (suffix == null) ? "errors.suffix" : suffix;
    }

    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }

    public boolean getErrors() {
        return (this.errors);
    }

    public void setErrors(boolean errors) {
        this.errors = errors;
    }
    
    public boolean getToken() {
        return (this.token);
    }

    public void setToken(boolean token) {
        this.token = token;
    }    
    // --------------------------------------------------------- Public Methods

    /**
     * Process the start tag.
     *
     * @throws JspException if a JSP exception has occurred
     */
    public int doStartTag() throws JspException {
        String key = this.key;

        if (key == null) {
            // Look up the requested property value
            Object value =
                TagUtils.getInstance().lookup(pageContext, name, property, scope);

            if ((value != null) && !(value instanceof String)) {
                JspException e =
                    new JspException(messages.getMessage("message.property", key));

                TagUtils.getInstance().saveException(pageContext, e);
                throw e;
            }

            key = (String) value;
        }

        // Construct the optional arguments array we will be using
        Object[] args = new Object[] { arg0, arg1, arg2, arg3, arg4 };

        // Retrieve the message string we are looking for
        String message =
            TagUtils.getInstance().message(pageContext, this.bundle,
                this.localeKey, key, args);

        if (message == null) {
            Locale locale =
                TagUtils.getInstance().getUserLocale(pageContext, this.localeKey);
            String localeVal =
                (locale == null) ? "default locale" : locale.toString();
            JspException e =
                new JspException(messages.getMessage("message.message",
                        "\"" + key + "\"",
                        "\"" + ((bundle == null) ? "(default bundle)" : bundle)
                        + "\"", localeVal));

            TagUtils.getInstance().saveException(pageContext, e);
            throw e;
        }
 
        ByteArrayOutputStream bs = new ByteArrayOutputStream();
        String output = bs.toString();        
        String errOutput = null;
        String tokenOutput = null;
        //check up error objects
        if ( errors == true )
        {
            // Were any error errMessages specified?
            ActionMessages errors = null;

            try {
                errors =
                    TagUtils.getInstance().getActionMessages(pageContext, errorname);
            } catch (JspException e) {
                TagUtils.getInstance().saveException(pageContext, e);
                throw e;
            }

            if (!((errors == null) || errors.isEmpty()))
            {
            	
           

            boolean headerPresent =
                TagUtils.getInstance().present(pageContext, bundle, locale,
                    getHeader());

            boolean footerPresent =
                TagUtils.getInstance().present(pageContext, bundle, locale,
                    getFooter());

            boolean prefixPresent =
                TagUtils.getInstance().present(pageContext, bundle, locale,
                    getPrefix());

            boolean suffixPresent =
                TagUtils.getInstance().present(pageContext, bundle, locale,
                    getSuffix());

            // Render the error errMessages appropriately
            StringBuffer results = new StringBuffer();
            boolean headerDone = false;
            String errMessage = null;
            Iterator reports =
                (errorproperty == null) ? errors.get() : errors.get(errorproperty);

            while (reports.hasNext()) {
                ActionMessage report = (ActionMessage) reports.next();

                if (!headerDone) {
                    if (headerPresent) {
                        errMessage =
                            TagUtils.getInstance().message(pageContext, bundle,
                                locale, getHeader());

                        results.append(errMessage);
                    }

                    headerDone = true;
                }

                if (prefixPresent) {
                    errMessage =
                        TagUtils.getInstance().message(pageContext, bundle, locale,
                            getPrefix());
                    results.append(errMessage);
                }

                if (report.isResource()) {
                    errMessage =
                        TagUtils.getInstance().message(pageContext, bundle, locale,
                            report.getKey(), report.getValues());
                } else {
                    errMessage = report.getKey();
                }

                if (errMessage != null) {
                    results.append(errMessage);
                }

                if (suffixPresent) {
                    errMessage =
                        TagUtils.getInstance().message(pageContext, bundle, locale,
                            getSuffix());
                    results.append(errMessage);
                }
            }

            if (headerDone && footerPresent) {
                errMessage =
                    TagUtils.getInstance().message(pageContext, bundle, locale,
                        getFooter());
                results.append(errMessage);
            }
            
            	errOutput =  results.toString();
            }

        }
        Object value = message;
        if ( token == true )
        {
        	HttpSession session = pageContext.getSession();
        	if ( session != null )
        	{
        		 tokenOutput = (String) session.getAttribute(Globals.TRANSACTION_TOKEN_KEY);        		 
        	}        	
        }
        HashMap oMap;
        if ( errOutput != null )
        {
        	oMap = new HashMap();
        	Object valCopy = message;
        	oMap.put("result", valCopy);
        	oMap.put("errors", errOutput);
        	if ( tokenOutput != null )
        	{
        		oMap.put("org.apache.struts.taglib.html.TOKEN", tokenOutput);
        	}
        	value = oMap;
        }
        else if ( tokenOutput != null )
        {
        	oMap = new HashMap();
        	Object valCopy = message;
        	oMap.put("result", valCopy);
        	oMap.put("org.apache.struts.taglib.html.TOKEN", tokenOutput);        	
        	value = oMap;
        }
        try
        {
        	if ( type.compareTo("amf") == 0 )
        	{
        		Amf3Output amfOp = new Amf3Output(new SerializationContext());
                amfOp.setOutputStream(bs);
        		amfOp.writeObject(value);
        		amfOp.close();
        	}        	
        }
        catch (IOException eio)
        {
        	System.err.println(eio.getMessage());
        }
        
        if ( type.compareTo("xml") == 0 )
        {
        	FxXMLOutput xmlEnc = new FxXMLOutput();
            String rootTagName = key;
            if ( rootTagName == null )
            	rootTagName = name;
            if ( rootTagName == null )
            	rootTagName = property;
            String filterRoot = TagUtils.getInstance().filter(rootTagName);
        	//output = "<?xml version=\"1.0\" encoding=\"utf-8\"?><" + rootTagName + ">" + xmlEnc.writeObject(value) + "</" + rootTagName + ">";
            output = "<?xml version=\"1.0\" encoding=\"utf-8\"?>";
        	if ( value instanceof Collection || value.getClass().isArray() )
        	{
        		output += xmlEnc.writeObject(value, filterRoot);
        	}
        	else
        	{
        		output += "<" + filterRoot + ">" + xmlEnc.writeObject(value) + "</" + filterRoot + ">";
        	}
        }
        if ( type.compareTo("amf") == 0 )
        {
        	try
        	{
        		pageContext.getResponse().getOutputStream().write(bs.toByteArray());;
        	}        	
        	catch (IOException e)
        	{
        		TagUtils.getInstance().write(pageContext, "IO Error occured while trying to AMF. Warning! This is an alpha release");
        	}
        	catch (IllegalStateException e1)
        	{
        		TagUtils.getInstance().write(pageContext, "You have characters in your jsp page. AMF means the jsp page should not output even a single newline. Please check your jsp file.");
        		
        	}
        }
        else
        {
        	TagUtils.getInstance().write(pageContext, output);
        }

        return (SKIP_BODY);
    }

    /**
     * Release any acquired resources.
     */
    public void release() {
        super.release();
        arg0 = null;
        arg1 = null;
        arg2 = null;
        arg3 = null;
        arg4 = null;
        bundle = Globals.MESSAGES_KEY;
        key = null;
        name = null;
        property = null;
        scope = null;
        localeKey = Globals.LOCALE_KEY;
        errors = false;
        token = false;
        type = "amf";
        errorname = Globals.ERROR_KEY;
        errorproperty = null;
        header = null;
        footer = null;
        prefix = null;
        suffix = null;
        locale = Globals.LOCALE_KEY;
    }
}
